import java.io._
import scala.io._
import scala.collection.mutable.MutableList

object RecipesApp {
    def main(args: Array[String]): Unit = {
        val recipes: RecipeRepo = FileRecipeRepo
        val operation = args(0)

        try {
            args match {
                case Array("-create", title) => recipes.add(new Recipe(title))
                case Array("-step", title, step) => recipes.save(recipes.get(title).addStep(step))
                case Array("-show", title) => show(recipes, title)
                case Array("-delete", title) => recipes.remove(recipes.get(title))
                case Array("-list", title) => recipes.list(title).foreach((r: Recipe) => println(r.title))
                case Array("-list") => recipes.list("").foreach((r: Recipe) => println(r.title))
                case _ => reportError("Illegal operation")
            }
        } catch {
            case e: Exception => reportError(e.getMessage());
        }
    }

    private def show(recipes: RecipeRepo, recipeTitle: String) {
        val recipe = recipes.get(recipeTitle)
        println(recipe.title)

        recipe.title.foreach((c: Char) => print(if (c == ' ') " " else "-"))
        println()

        if (recipe.steps.isEmpty) {
            println("No se han registrado los pasos para realizar esta receta")
        } else {
            for ((step, index) <- recipe.steps.zipWithIndex) {
                println(index + 1 + ") " + step)
            }
        }
    }

    private def reportError(message: String) = { System.out.println(message); System.exit(0); }

    private def withPrintWriter(file: File)(op: PrintWriter => Unit) {
        val writer = new PrintWriter(file)
        try {
            op(writer)
        } finally {
            writer.close()
        }
    }
}

/**
 * Repository for recipes.
 */
trait RecipeRepo {

    /**
     * Adds the given recipe to the repository.
     */
    def add(recipe: Recipe)

    /**
     * Saves al already registered recipe
     */
    def save(recipe: Recipe)

    /**
     * Deletes the given recipe
     */
    def remove(recipe: Recipe)

    /**
     * Retrieves the recipe with the indicated name.
     */
    def get(name: String): Recipe

    /**
     * Retrieves all the recipes partially matching the given title filter.
     */
    def list(filter: String): Iterable[Recipe]
}

object FileRecipeRepo extends RecipeRepo {

    private val recipesDir = "recipes"
    private val extension = ".rp"

    private val dir = {
        val result = new File(recipesDir)
        if (!result.exists()) {
            result.mkdir()
        }
        result
    }

    override def add(recipe: Recipe) {
        val file = new File(dir, buildName(recipe.title))
        if (file.exists()) {
            throw new RuntimeException("Recipe \"" + recipe.title + "\" already exists")
        } else {
            file.createNewFile()
        }
    }

    override def save(recipe: Recipe) {
        withWriter(new FileWriter(getFile(recipe.title), false)) { writer =>
            recipe.steps.foreach((step: String) => writer.write(step + "\n"))
        }
    }

    override def remove(recipe: Recipe) { getFile(recipe.title).delete() }

    override def get(recipe: String): Recipe = {
        val r = new Recipe(recipe)

        for (line <- Source.fromFile(getFile(recipe)).getLines()) {
            r.addStep(line)
        }

        return r;
    }

    override def list(nameFilter: String): List[Recipe] = {
        val recipes = for {
        	recipe <- dir.list()
        	if(recipe.toLowerCase().contains(nameFilter))
        } yield get(recipe.substring(0, recipe.size - 3))
        
        return List.fromArray(recipes).sorted
    }

    private def withWriter(writer: Writer)(op: Writer => Unit) {
        try {
            op(writer)
        } finally {
            writer.close()
        }
    }

    private def getFile(recipeName: String): File = {
        val result = new File(dir, buildName(recipeName))

        if (!result.exists()) {
            throw new RuntimeException("Recipe \"" + recipeName + "\" does not exist!")
        }

        return result
    }

    private def buildName(recipe: String) = recipe + extension
}


class Recipe(val title: String) extends Ordered[Recipe] {

    val steps = new MutableList[String]();

    def addStep(step: String): Recipe = { steps += step; return this }
    
    def compare(that: Recipe) = title.compare(that.title)
}